#include <math.h>
#include <wiiuse/wpad.h>
#include <stdio.h>
#include <asndlib.h>
#include <fcntl.h>
#include "oggplayer.h"
#include "gameaction.h"
#include "gametrigger.h"
#include "missilecommand.h"
#include "textactor.h"
#define MEDIAPATH "/APPS/missilecmd/media/"

Game::Game()
{
  // Load our media assets
  imgload.load(MEDIAPATH"missile.png");
  imgload.load(MEDIAPATH"silo.png");
  imgload.load(MEDIAPATH"shockwave.png");
  imgload.load(MEDIAPATH"crosshairs.png");
  imgload.load(MEDIAPATH"grass.png");
  imgload.load(MEDIAPATH"silodome.png");
  imgload.load(MEDIAPATH"targetred.png");
  imgload.load(MEDIAPATH"targetyellow.png");
  imgload.load(MEDIAPATH"dashcircle.png");
  imgload.load(MEDIAPATH"black.png");
  imgload.load(MEDIAPATH"blue.png");
  imgload.load(MEDIAPATH"red.png");
  imgload.load(MEDIAPATH"title.png");

  pcmload.load(MEDIAPATH"firemissile.raw");
  pcmload.load(MEDIAPATH"siloexplode.raw");
  pcmload.load(MEDIAPATH"shockwaveboom.raw");

  modaltext = new TextActor();
}

Game::~Game()
{
  set<Actor*>::iterator it;

  for(it = actors.begin(); it != actors.end(); ++it)
    delete *it;
  delete modaltext;
}

void Game::update()
{
  if(enablePlayerControl) {
    doMoveSilos();
    doFirePlayerMissile();
  }
  if(!pause) {
    updateAllActors();
    replenishEnemyMissiles();
    handleSplitMissiles();
    handleCollisions();
    handleExplosions();
    cleanupActors();
  }
  updateFade();
  
  ++t;
}

void Game::setFade(f32 fade, bool force)
{
  if(force)
    this->fade = fade;
  targetfade = fade;
}

void Game::updateFade()
{
  if(fade < targetfade) {
    fade += .03;
    if(fade > targetfade)
      fade = targetfade;
  } else if(fade > targetfade) {
    fade -= .03;
    if(fade < targetfade)
      fade = targetfade;
  }
}

const f32 SILO1X = 100;
const f32 SILO2X = 320;
const f32 SILO3X = 540;
const f32 SILOY = 430;

u32 MAXMISSILES = 10;

void Game::initialize()
{
  set<Actor*>::iterator it;
  set<SiloActor*>::iterator sit;

  for(it = actors.begin(); it != actors.end(); ++it)
    delete *it;
  actors.clear();
  silos.clear();
  missiles.clear();
  playermissiles.clear();
  enemymissiles.clear();
  shockwaves.clear();
  modaltext->set("", 0, 0);

  // Create silos
  siloleft = new SiloActor(SILO1X,SILOY);
  siloleft->SetImage(imgload.get(MEDIAPATH"silo.png"));
  actors.insert(siloleft);
  silos.insert(siloleft);
  silodomeleft.SetImage(imgload.get(MEDIAPATH"silodome.png"));
  silodomeleft.SetPosition(siloleft->getX()-32,siloleft->getY()-32);

  silocenter = new SiloActor(SILO2X,SILOY);
  silocenter->SetImage(imgload.get(MEDIAPATH"silo.png"));
  actors.insert(silocenter);
  silos.insert(silocenter);
  silodomecenter.SetImage(imgload.get(MEDIAPATH"silodome.png"));
  silodomecenter.SetPosition(silocenter->getX()-32,silocenter->getY()-32);

  siloright = new SiloActor(SILO3X,SILOY);
  siloright->SetImage(imgload.get(MEDIAPATH"silo.png"));
  actors.insert(siloright);
  silos.insert(siloright);
  silodomeright.SetImage(imgload.get(MEDIAPATH"silodome.png"));
  silodomeright.SetPosition(siloright->getX()-32,siloright->getY()-32);
  
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    SiloActor *silo = *sit;
    silo->healthbar.SetImage(imgload.get(MEDIAPATH"red.png"));
    silo->healthbar.SetPosition(silo->getX()-2,silo->getY()+20);
    silo->healthbar.SetStretchHeight(2);
    silo->ammobar.SetImage(imgload.get(MEDIAPATH"blue.png"));
    silo->ammobar.SetPosition(silo->getX()-2,silo->getY()+30);
    silo->ammobar.SetStretchHeight(2);
  }

  // Create grass
  grass.SetImage(imgload.get(MEDIAPATH"grass.png"));
  grass.SetPosition(0,SILOY);
  
  // Create crosshairs
  crosshairs.SetImage(imgload.get(MEDIAPATH"crosshairs.png"));
  
  enablePlayerControl = true;
  enableSilos = true;
  enableMissiles = true;
  enableShockwaves = true;
  enableHealthBar = true;
  enableAmmoBar = true;
  enableMissileTargets = true;
  enableBackground = true;
  enableCrosshairs = true;
  enableConsole = true;
  enableText = false;

  pause = false;
  
  t = 0;
  
  fade = 0;
  targetfade = 0;
}

void Game::activate()
{
  set<SiloActor*>::iterator it;
  for(it = silos.begin(); it != silos.end(); ++it)
    (*it)->activate();
}

void Game::doMoveSilos()
{
  set<SiloActor*>::iterator sit;

  // Set the silo aim
  for(sit = silos.begin(); sit != silos.end(); ++sit)
    (*sit)->aimAt(px, py);
}

MissileActor *Game::doFireMissileAt(f32 xs, f32 ys, f32 xf, f32 yf, MissileActor::Type owner, int splitafter)
{
  MissileActor *missile = new MissileActor(xs,ys,xf,yf,owner,splitafter);
  actors.insert(missile);
  missiles.insert(missile);
  missile->SetImage(imgload.get(MEDIAPATH"missile.png"));
  missile->activate();
  if(owner == MissileActor::PLAYER) {
    playermissiles.insert(missile);
    missile->missileTarget.SetPosition(xf-8, yf-8);
    missile->missileTarget.SetZoom(2.0);
    missile->missileLine.SetImage(imgload.get(MEDIAPATH"blue.png"));
    missile->missileLine.SetRotation(missile->getRotation()/2.0);
    missile->missileLine.SetStretchWidth(.5);
  } else {
    enemymissiles.insert(missile);
    missile->missileCircle.SetImage(imgload.get(MEDIAPATH"dashcircle.png"));
    missile->missileCircle.SetZoom(0.7);
    missile->missileLine.SetImage(imgload.get(MEDIAPATH"red.png"));
    missile->missileLine.SetRotation(missile->getRotation()/2.0);
    missile->missileLine.SetStretchWidth(.5);
  }
  
  return missile;
}

void Game::doFirePlayerMissile()
{
  set<SiloActor*>::iterator sit;

  if(!enableCrosshairs)
    return;

  // Fire player missile
  if((WPAD_ButtonsDown(0) & WPAD_BUTTON_B) ||
     ((WPAD_Data(0)->exp.type == EXP_NUNCHUK) &&
     (WPAD_ButtonsDown(0) & WPAD_NUNCHUK_BUTTON_Z))) {
    SiloActor *firingSilo = NULL;

    // Use the closest silo
    for(sit = silos.begin(); sit != silos.end(); ++sit) {
      SiloActor *silo = *sit;
      if(!silo->canLaunch())
        continue;
      if(!firingSilo || abs(silo->getX()-px) < abs(firingSilo->getX()-px))
        firingSilo = silo;
    }

    // Unless they're holding a D-pad direction to select a silo
    if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_LEFT && siloleft->canLaunch())
      firingSilo = siloleft;
    else if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP && silocenter->canLaunch())
      firingSilo = silocenter;
    else if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT && siloright->canLaunch())
      firingSilo = siloright;
    else 

    // Or holding the nunchuck stick in a direction
    if(WPAD_Data(0)->exp.type == EXP_NUNCHUK &&
       WPAD_Data(0)->exp.nunchuk.js.mag > .5) {
      f32 ang = WPAD_Data(0)->exp.nunchuk.js.ang;
      if(ang > 225 && ang < 315 && siloleft->canLaunch())
        firingSilo = siloleft;
      else if((ang >= 315 || ang < 45) && silocenter->canLaunch())
        firingSilo = silocenter;
      else if(ang >= 45 && ang < 135 && siloright->canLaunch())
        firingSilo = siloright;
    }
    
    if(firingSilo) {
      pcmload.play(MEDIAPATH"firemissile.raw");
      firingSilo->fireMissile();
      doFireMissileAt(firingSilo->getX(),SILOY,px,py,MissileActor::PLAYER);
    }
  }
}

void Game::updateAllActors()
{
  // Update everything
  set<Actor*>::iterator it;
  for(it = actors.begin(); it != actors.end(); ++it)
    (*it)->update();
}

void Game::replenishEnemyMissiles()
{
  set<MissileActor*>::iterator mit;

  // Launch enemy missiles (limit to 1 per frame)
  if(enemymissiles.size() < MAXMISSILES) {
    f32 xs = rand() % 640;
    f32 xf = rand() % 640;
    bool dosplit = rand() % 10 == 0; // 1 in 10
    int splitafter = -1;
    if(dosplit)
      splitafter = rand() % 200 + 160;
    doFireMissileAt(xs, -60, xf, SILOY, MissileActor::ENEMY, splitafter);
  }
}

void Game::handleSplitMissiles()
{
  set<MissileActor*>::iterator mit;

  // Split missiles
  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(missile->shouldSplit()) {
      int splitcount = rand() % 4 + 2; // 2 to 5 submissiles
      missile->split(splitcount);
      for(int i = 0; i < splitcount; i++) {
        f32 targetx = rand() % 640;
        MissileActor::Type owner = missile->getOwner();
        MissileActor *splitmissile = new MissileActor(missile->getX(), missile->getY(),
            targetx, SILOY, owner);
        actors.insert(splitmissile);
        missiles.insert(splitmissile);
        if(owner == MissileActor::ENEMY)
          enemymissiles.insert(splitmissile);
        else
          playermissiles.insert(splitmissile);
        splitmissile->setSplitParent(missile);
        splitmissile->SetImage(imgload.get(MEDIAPATH"missile.png"));
        splitmissile->activate();
        splitmissile->missileCircle.SetImage(imgload.get(MEDIAPATH"dashcircle.png"));
        splitmissile->missileCircle.SetZoom(0.7);
        if(owner == MissileActor::ENEMY)
          splitmissile->missileLine.SetImage(imgload.get(MEDIAPATH"red.png"));
        else
          splitmissile->missileLine.SetImage(imgload.get(MEDIAPATH"blue.png"));
        splitmissile->missileLine.SetRotation(splitmissile->getRotation()/2.0);
        splitmissile->missileLine.SetStretchWidth(.5);
      }
    }
  }
}

void Game::handleCollisions()
{
  set<SiloActor*>::iterator sit;
  set<MissileActor*>::iterator mit;
  set<ShockwaveActor*>::iterator swit;

  // Missile-shockwave collisions
  for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
    f32 mx,my,sx,sy,r;
    ShockwaveActor *shock = *swit;
    sx = shock->getX();
    sy = shock->getY();
    r = shock->getR();
    if(sy < 60)
      continue;
    for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
      MissileActor *missile = *mit;
      mx = missile->getX();
      my = missile->getY();
      if((mx-sx)*(mx-sx)+(my-sy)*(my-sy) < r*r)
        missile->explode();
    }
  }
  
  // Missile-base collisions
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    f32 mx,my,sx,sy,r;
    SiloActor *silo = *sit;
    sx = silo->getX();
    sy = silo->getY();
    r = 32;
    for(mit = enemymissiles.begin(); mit != enemymissiles.end(); ++mit) {
      MissileActor *missile = *mit;
      mx = missile->getX();
      my = missile->getY();
      if((mx-sx)*(mx-sx)+(my-sy)*(my-sy) < r*r) {
        pcmload.play(MEDIAPATH"siloexplode.raw");
        missile->explode();
        silo->directHit();
      }
    }
  }

  // Base-shockwave collisions
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    f32 swx,swy,swr,sx,sy,sr;
    SiloActor *silo = *sit;
    sx = silo->getX();
    sy = silo->getY();
    sr = 32;
    for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
      ShockwaveActor *shock = *swit;
      swx = shock->getX();
      swy = shock->getY();
      swr = shock->getR();
      if((swx-sx)*(swx-sx)+(swy-sy)*(swy-sy) < (sr+swr)*(sr+swr))
        silo->splashHit();
    }
  }
}

void Game::handleExplosions()
{
  set<MissileActor*>::iterator mit;

  // Handle explosions
  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(missile->isExploded()) {
      pcmload.play(MEDIAPATH"shockwaveboom.raw");
      ShockwaveActor *shock = new ShockwaveActor(missile->getX(), missile->getY());
      shockwaves.insert(shock);
      actors.insert(shock);
      shock->SetImage(imgload.get(MEDIAPATH"shockwave.png"));
      shock->activate();
    }
  }
}

void Game::cleanupActors()
{
  set<MissileActor*>::iterator mit;
  set<ShockwaveActor*>::iterator swit;
  set<MissileActor*> mtoerase;
  set<ShockwaveActor*> stoerase;

  // Missile cleanup
  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(missile->isFinished())
      mtoerase.insert(missile);
  }
  for(mit = mtoerase.begin(); mit != mtoerase.end(); ++mit) {
    MissileActor *missile = *mit;
    playermissiles.erase(missile);
    enemymissiles.erase(missile);
    missiles.erase(missile);
    actors.erase(missile);
    delete missile;
  }

  // Shockwave cleanup
  for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
    ShockwaveActor *shock = *swit;
    if(shock->isFinished())
      stoerase.insert(shock);
  }
  for(swit = stoerase.begin(); swit != stoerase.end(); ++swit) {
    ShockwaveActor *shock = *swit;
    shockwaves.erase(shock);
    actors.erase(shock);
    delete shock;
  }
}

void Game::draw()
{
  set<MissileActor*>::iterator mit;
  set<SiloActor*>::iterator sit;
  set<ShockwaveActor*>::iterator swit;
  
  if(enableShockwaves) {
    for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
      ShockwaveActor *shock = *swit;
      shock->SetPosition(shock->getX()-32, shock->getY()-32);
      shock->SetZoom(shock->getR()/32.0);
      shock->SetTransparency(128);
      shock->Draw();
    }
  }
  
  if(enableMissileTargets) {
    for(mit = playermissiles.begin(); mit != playermissiles.end(); ++mit) {
      MissileActor *missile = *mit;
      if(missile->getT()/3 % 2)
        missile->missileTarget.SetImage(imgload.get(MEDIAPATH"targetred.png"));
      else
        missile->missileTarget.SetImage(imgload.get(MEDIAPATH"targetyellow.png"));
      missile->missileTarget.Draw();
    }
  }
  
  if(enableMissiles) {
    for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
      MissileActor *missile = *mit;
      f32 sx = missile->getStartX();
      f32 sy = missile->getStartY();
      f32 mx = missile->getX();
      f32 my = missile->getY();
      f32 len = sqrt((sx-mx)*(sx-mx)+(sy-my)*(sy-my));
      f32 linex = (mx+sx)/2;
      f32 liney = (my+sy)/2;
      missile->missileLine.SetPosition(linex-2,liney-2);
      missile->missileLine.SetStretchHeight(len/4.0);
      missile->missileLine.Draw();
    }
    for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
      MissileActor *missile = *mit;
      if(!missile->isSplit() && missile->getOwner() == MissileActor::ENEMY) {
        missile->missileCircle.SetPosition(missile->getX()-32, missile->getY()-32);
        missile->missileCircle.SetRotation(missile->missileCircle.GetRotation()+1.5);
        missile->missileCircle.Draw();
      }
    }
    for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
      MissileActor *missile = *mit;
      if(!missile->isSplit()) {
        missile->SetPosition(missile->getX()-2, missile->getY()-4);
        missile->SetRotation(missile->getRotation()/2.0);
        missile->Draw();
      }
    }
  }

  if(enableSilos) {
    for(sit = silos.begin(); sit != silos.end(); ++sit) {
      SiloActor *silo = *sit;
      silo->SetPosition(silo->getX()-8, silo->getY()-32);
      silo->SetRotation(silo->getRotation()/2.0);
      silo->SetZoom(1.5);
      silo->Draw();
    }
    silodomeleft.Draw();
    silodomecenter.Draw();
    silodomeright.Draw();
  }

  if(enableBackground) {
    grass.Draw();
  }
  
  if(enableHealthBar) {
    for(sit = silos.begin(); sit != silos.end(); ++sit) {
      SiloActor *silo = *sit;
      silo->healthbar.SetStretchWidth(silo->getHealthPct()*30.0);
      silo->healthbar.Draw();
    }
  }

  if(enableAmmoBar) {
    for(sit = silos.begin(); sit != silos.end(); ++sit) {
      SiloActor *silo = *sit;
      silo->ammobar.SetStretchWidth(silo->getAmmoPct()*30.0);
      silo->ammobar.Draw();
    }
  }
  
  static Sprite fadebox;
  fadebox.SetImage(imgload.get(MEDIAPATH"black.png"));
  fadebox.SetPosition(320-2,240-2);
  fadebox.SetStretchWidth(800.0/4.0);
  fadebox.SetStretchHeight(600.0/4.0);
  fadebox.SetTransparency((1.0-fade) * 255.0);
  fadebox.Draw();  

  if(enableText) {
	modaltext->Draw();
  }

  if(enableCrosshairs) {
    static int rot = 0;
    crosshairs.SetPosition(px-8, py-8);
    crosshairs.SetRotation(rot);
    crosshairs.SetZoom(5.0);
    crosshairs.Draw();
    rot+=3;
  }
}
